# -*- coding: utf-8 -*-
"""
查看压力测试结果

使用方法：
    python view_stress_results.py storage/stress_test_detail_20251117_170000.csv
"""
import sys
import pandas as pd
import matplotlib
matplotlib.use('Agg')  # 使用非GUI后端
import matplotlib.pyplot as plt
from pathlib import Path

def analyze_stress_results(csv_path):
    """分析压力测试结果"""
    print("=" * 60)
    print("压力测试结果分析")
    print("=" * 60)
    
    # 读取CSV
    df = pd.read_csv(csv_path)
    
    print(f"\n📊 文件: {csv_path}")
    print(f"📝 总测试数: {len(df)}")
    
    # 基本统计
    total = len(df)
    successful = df['success'].sum()
    understood = df['understood'].sum()
    
    print(f"\n✅ 成功: {successful}/{total} ({successful/total*100:.1f}%)")
    print(f"🧠 理解: {understood}/{total} ({understood/total*100:.1f}%)")
    
    # 响应时间统计
    print(f"\n⏱️  响应时间统计:")
    print(f"   平均: {df['elapsed_time'].mean():.2f}秒")
    print(f"   中位数: {df['elapsed_time'].median():.2f}秒")
    print(f"   最小: {df['elapsed_time'].min():.2f}秒")
    print(f"   最大: {df['elapsed_time'].max():.2f}秒")
    print(f"   标准差: {df['elapsed_time'].std():.2f}秒")
    
    # 按线程统计
    print(f"\n🧵 按线程统计:")
    thread_stats = df.groupby('thread_id').agg({
        'success': ['sum', 'count'],
        'elapsed_time': ['mean', 'min', 'max']
    }).round(2)
    
    for thread_id in df['thread_id'].unique():
        thread_data = df[df['thread_id'] == thread_id]
        success_count = thread_data['success'].sum()
        total_count = len(thread_data)
        avg_time = thread_data['elapsed_time'].mean()
        print(f"   线程{thread_id}: {success_count}/{total_count} 成功, 平均 {avg_time:.2f}秒")
    
    # 响应时间分布
    print(f"\n📈 响应时间分布:")
    bins = [0, 1, 2, 3, 5, 10, float('inf')]
    labels = ['<1s', '1-2s', '2-3s', '3-5s', '5-10s', '>10s']
    df['time_range'] = pd.cut(df['elapsed_time'], bins=bins, labels=labels)
    time_dist = df['time_range'].value_counts().sort_index()
    
    for label, count in time_dist.items():
        percentage = count / len(df) * 100
        bar = '█' * int(percentage / 2)
        print(f"   {label:6s}: {count:3d} ({percentage:5.1f}%) {bar}")
    
    # 失败的测试
    failed = df[df['success'] == False]
    if len(failed) > 0:
        print(f"\n❌ 失败的测试 ({len(failed)}个):")
        for idx, row in failed.head(5).iterrows():
            print(f"   [线程{row['thread_id']}] {row['message'][:30]}...")
            if row['error']:
                print(f"      错误: {row['error'][:60]}...")
    
    # 生成图表（可选）
    try:
        generate_charts(df, csv_path)
    except Exception as e:
        print(f"\n⚠️  图表生成失败: {e}")
    
    print("\n" + "=" * 60)


def generate_charts(df, csv_path):
    """生成可视化图表"""
    output_dir = Path(csv_path).parent
    base_name = Path(csv_path).stem
    
    # 配置中文字体
    try:
        # macOS系统字体
        plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
        plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    except:
        pass
    
    # 创建图表
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))
    fig.suptitle('Stress Test Results Analysis', fontsize=16)  # 使用英文避免字体问题
    
    # 1. 响应时间分布直方图
    axes[0, 0].hist(df['elapsed_time'], bins=20, edgecolor='black', alpha=0.7)
    axes[0, 0].set_xlabel('Response Time (seconds)')
    axes[0, 0].set_ylabel('Frequency')
    axes[0, 0].set_title('Response Time Distribution')
    axes[0, 0].axvline(df['elapsed_time'].mean(), color='r', linestyle='--', label=f'Avg: {df["elapsed_time"].mean():.2f}s')
    axes[0, 0].legend()
    
    # 2. 按线程的响应时间箱线图
    thread_ids = sorted(df['thread_id'].unique())
    thread_data = [df[df['thread_id'] == tid]['elapsed_time'].values for tid in thread_ids]
    thread_labels = [f'线程{i}' for i in thread_ids]
    
    # 使用新的参数名 tick_labels（兼容旧版本）
    try:
        axes[0, 1].boxplot(thread_data, tick_labels=thread_labels)
    except TypeError:
        # 如果是旧版本matplotlib，使用labels参数
        axes[0, 1].boxplot(thread_data, labels=thread_labels)
    
    axes[0, 1].set_ylabel('Response Time (seconds)')
    axes[0, 1].set_title('Response Time by Thread')
    axes[0, 1].grid(True, alpha=0.3)
    
    # 3. 成功率饼图
    success_counts = df['success'].value_counts()
    # 动态生成labels，只包含实际存在的类别
    pie_labels = []
    pie_values = []
    if True in success_counts.index:
        pie_labels.append('Success')
        pie_values.append(success_counts[True])
    if False in success_counts.index:
        pie_labels.append('Failed')
        pie_values.append(success_counts[False])
    
    axes[1, 0].pie(pie_values, labels=pie_labels, autopct='%1.1f%%', startangle=90)
    axes[1, 0].set_title('Success Rate')
    
    # 4. 时间序列图
    df_sorted = df.sort_values('timestamp')
    df_sorted['test_number'] = range(len(df_sorted))
    for thread_id in sorted(df['thread_id'].unique()):
        thread_df = df_sorted[df_sorted['thread_id'] == thread_id]
        axes[1, 1].plot(thread_df['test_number'], thread_df['elapsed_time'], 
                       marker='o', label=f'Thread {thread_id}', alpha=0.7)
    axes[1, 1].set_xlabel('Test Number')
    axes[1, 1].set_ylabel('Response Time (seconds)')
    axes[1, 1].set_title('Response Time Timeline')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)
    
    plt.tight_layout()
    
    # 保存图表
    chart_path = output_dir / f"{base_name}_charts.png"
    plt.savefig(chart_path, dpi=150, bbox_inches='tight')
    plt.close()  # 关闭图表释放内存
    print(f"\n📊 图表已保存: {chart_path}")


def main():
    if len(sys.argv) < 2:
        # 查找最新的压力测试结果文件
        storage_path = Path("storage")
        csv_files = list(storage_path.glob("stress_test_detail_*.csv"))
        
        if not csv_files:
            print("❌ 未找到压力测试结果文件")
            print("使用方法: python view_stress_results.py <csv文件路径>")
            print("或者先运行: python stress_test_llm.py")
            return
        
        # 使用最新的文件
        csv_path = max(csv_files, key=lambda p: p.stat().st_mtime)
        print(f"📂 使用最新的测试结果: {csv_path}\n")
    else:
        csv_path = Path(sys.argv[1])
        
        if not csv_path.exists():
            print(f"❌ 文件不存在: {csv_path}")
            return
    
    analyze_stress_results(csv_path)


if __name__ == "__main__":
    main()
